package semant.second_pass;

import semant.Env;
import symbol.ClassInfo;
import symbol.MethodInfo;
import symbol.Symbol;
import symbol.VarInfo;
import syntaxtree.MethodDecl;
import syntaxtree.Statement;
import syntaxtree.Type;
import syntaxtree.VisitorAdapter;
import util.List;

public class MethodHandler extends VisitorAdapter {

	public static void secondPass(Env env, ClassInfo ci, MethodDecl node) {
		Symbol name = Symbol.symbol(node.name.s);
		MethodInfo mi = ci.methods.get(name);
		
		ClassInfo base = ci.base;
		MethodInfo mbase;
		
		//busca pelo metodo na hieraquia de classes
		while (base != null) {
			mbase = base.methods.get(name);
			
			//se o metodo esta definido, verifica-se o tipo de retorno e numero e tipo dos parametros
			if (mbase != null) {
				if (!TypeHandler.compatible(env, mbase.type, mi.type)) {
					env.err.Error(node, new Object[]{
						"Tipo de retorno do metodo: \'" + name + "\' invalido",
						"Esperado: " +  mi.type,
						"Encontrado: " + mbase.type
					});
				} else if (mbase.formals != null && mi.formals != null) {
					if (mbase.formals.size() != mi.formals.size()) {
						env.err.Error(node, new Object[]{
							"Numero de parametros do metodo reimplementado \'" + name + "\' difere da classe pai",
							"Esperado: " +  mbase.formals.size(),
							"Encontrado: " + mi.formals.size()
						});
					} else {
						List<VarInfo> mbaseFormals;
						List<VarInfo> miFormals ;
						
						for (mbaseFormals = mbase.formals, miFormals = mi.formals;
							mbaseFormals != null && miFormals != null;
							mbaseFormals = mbaseFormals.tail, miFormals = miFormals.tail) {
							
							if (!TypeHandler.compatible(env, miFormals.head.type , mbaseFormals.head.type)) {
								env.err.Error(node, new Object[]{
									"Tipo invalido do parametro \'" + miFormals.head.name + "\' do metodo \'" + name,
									"Esperado: " +  mbaseFormals.head.type,
									"Encontrado: " + miFormals.head.type
								});
							}
						}
						
					}
				} else if (mbase.formals != null) {
					env.err.Error(node, new Object[]{
						"Numero de parametros do metodo reimplementado \'" + name + "\' difere da classe pai",
						"Esperado: " +  mbase.formals.size(),
						"Encontrado: 0"
					});
				} else if (mi.formals != null) {
					env.err.Error(node, new Object[]{
						"Numero de parametros do metodo reimplementado \'" + name + "\' difere da classe pai",
						"Esperado: 0",
						"Encontrado: " + mbase.formals.size()
					});
				}
				break;
			}
			base = base.base;
		}
		
		
		for (List<Statement> v = node.body; v != null; v = v.tail) {
            StatementHandler.secondPass(env, ci, mi, v.head);
        }
	}

}
